package com.foshanshop.ejb3.dao;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;

import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.SessionFactory;

import com.dhgate.log.Log;
import com.dhgate.log.LogFactory;
import com.dhgate.persist.HibernateHelper;
import com.dhgate.persist.Session;

/**
 * 抽象dao工厂类，实现dao的创建和Session的注入
 * 
 * @author yuxiaowei
 * @version 2.1
 */
public abstract class AbstractDAOFactory2 {

    Log log = LogFactory.getLogger(AbstractDAOFactory2.class);

    private static String defaultSFKey = null;

    private static Map<String, String> mapDaos = null;

    private static Map<String, SessionFactory> mapSessionFactroys = null;

    /**
     * 配置文件名称
     */
    private static final String MULTIDS_CONFIG_FILE = "multids.xml";

    /**
     * threadlocal保证每个线程有自己的dao，并且只会产生一个dao 这样的设计是为了减小dao类的频繁创建，提高性能
     */
    private ThreadLocal<HashMap<String, Object>> threadLocal = new ThreadLocal<HashMap<String, Object>>();

    // singleton pattern
    public AbstractDAOFactory2() {

        if (null == mapDaos || null == mapSessionFactroys) {
            init();
        }
    }

    // 初始化 读取config文件；构造mapDaos、mapSessionFactroys对象；
    private void init() {
        try {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            URL url = loader.getResource(MULTIDS_CONFIG_FILE);
            if (url == null) {
                log.error("no find dao-sessionFactory.xml File , please put dao-sessionFactory.xml in your classpath!");
                throw new RuntimeException("no find dao-sessionFactory.xml File , please put dao-sessionFactory.xml in your classpath!");
            }
            loadDom(url);
            log.info(new StringBuilder().append("load dao-sessionFactory.xml config file from :").append(url.getFile()).toString());
        } catch (Exception ex) {
            mapDaos = null;
            mapSessionFactroys = null;
            defaultSFKey = null;
            log.error("dao-sessionFactory loadConfig error !");
            throw new RuntimeException("dao-sessionFactory loadConfig error !", ex);
        }
    }

    /**
     * 通过JNDI得到SessionFactory
     * 
     * @param jndisf
     * @return
     */
    private SessionFactory getSessionFactoryByJndi(String jndisf) {
        SessionFactory sessionFactory = null;
        boolean productmode = false;
        try {
            // product mode
            sessionFactory = (SessionFactory) new InitialContext().lookup(jndisf);
            productmode = true;
        } catch (NameNotFoundException ex) {
             log.error(ex);
            // ignore just for flowing code to run
        } catch (NamingException ex) {
             log.error(ex);
            // ignore just for flowing code to run
        }

        if (!productmode) {
            // 说明是测试测试环境
            // build sessionfactory for junit testing case
            sessionFactory = HibernateHelper.getSessionFactory();
            log.info("Use sessionFactory in development mode");
        }

        if (sessionFactory == null) {
            throw new RuntimeException(
                    "can't not get sessionFactory , maybe you never deploy dhgate-hibernate.har to jboss ,please check it!!!");
        }

        return sessionFactory;
    }

    /**
     * 装载配置文件
     * 
     * @param url
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    private void loadDom(URL url) throws Exception {
        try {
            DocumentFactory df = DocumentFactory.getInstance();
            // 取得XML路
            SAXReader reader = new SAXReader(df);
            // 获得XML文档对象
            Document document = reader.read(url);
            Element root = document.getRootElement();
            for (Iterator i = root.elementIterator(); i.hasNext();) {
                Element element = (Element) i.next();
                String tag = element.getName();
                if (tag.equalsIgnoreCase("dss")) {
                    mapSessionFactroys = new HashMap<String, SessionFactory>();
                    for (Iterator sub = element.elementIterator("ds"); sub.hasNext();) {
                        Element subElement = (Element) sub.next();
                        if (subElement.attribute("name") != null && subElement.getText() != null) {
                            String name = subElement.attribute("name").getValue().trim();
                            if (null != mapSessionFactroys.get(name)) {
                                log.warn("This ds Name " + name + "Is Exists!");
                            }
                            mapSessionFactroys.put(name, getSessionFactoryByJndi(subElement.getText().trim()));
                            log.info("This ds Name " + name + " was Loaded!");
                        }
                    }
                } else if (tag.equalsIgnoreCase("daos")) {
                    mapDaos = new HashMap<String, String>();
                    if (element.attribute("defaultds") != null) {
                        defaultSFKey = element.attribute("defaultds").getValue().trim();
                        if (null == this.mapSessionFactroys.get(defaultSFKey)) {
                            defaultSFKey = null;
                            throw new RuntimeException("defaultds name can't not be null!");
                        }
                    } else {
                        throw new RuntimeException("defaultds name can't not be null!");
                    }
                    for (Iterator sub = element.elementIterator("dao"); sub.hasNext();) {
                        Element subElement = (Element) sub.next();
                        if (subElement.attribute("classname") != null && subElement.attribute("ds") != null) {
                            String className = subElement.attribute("classname").getValue().trim();
                            String sessionFactoryKey = subElement.attribute("ds").getValue().trim();
                            if (null != mapDaos.get(className)) {
                                log.warn("This Dao ClassName " + className + "Is Exists!");
                            }
                            mapDaos.put(className, sessionFactoryKey);
                            log.info("This Dao ClassName " + className + "Is Loaded!");
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error(new StringBuilder("mutilds loadConfig error !").append(" config url :").append(url.getFile()).toString());
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取SessionFactory通过Dao的Class
     * 
     * @param daoClass
     * @return
     */
    @SuppressWarnings("unchecked")
    public static SessionFactory getDaoToSessionFactory(Class daoClass) {
        SessionFactory sf = null;
        String sessionFactoryKey = mapDaos.get(daoClass.getName().trim());
        if (null != sessionFactoryKey && !sessionFactoryKey.equals("")) {
            sf = mapSessionFactroys.get(sessionFactoryKey);
            // log.debug(daoClass.getName() + "find dao to sessionFactory!");
        } else {
            sf = mapSessionFactroys.get(defaultSFKey);
            // log.debug(daoClass.getName() + "no find dao to sessionFactory,
            // use default sessionFactory" + mapDaos.get(defaultSFKey) + " !");
        }
        return sf;
    }

    /**
     * 获取Dao
     * 
     * @param <T>
     * @param a
     *            Dao对应的Class
     * @param falg
     *            false:：不对HibernateDao的Session赋值，true:对HibernateDao中Session赋值
     * @return Dao对象
     */
    public <T> T getDAO(Class<T> a, Boolean falg) {

        HashMap<String, Object> daomaps = threadLocal.get();
        if (daomaps == null) {
            daomaps = new HashMap<String, Object>();
        }

        String key = a.getName();
        T cacheInstance = (T) daomaps.get(key);

        if (cacheInstance == null) {
            try {
                cacheInstance = a.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("InstantiationException", e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("IllegalAccessException", e);
            }
            daomaps.put(key, cacheInstance);
            threadLocal.set(daomaps);
            log.debug("newInstance  " + key + " and put ThreadLocal;");
        }
        if (falg) {
            try {
                Method setSession;
                setSession = a.getMethod("setSession", Session.class);
                setSession.invoke(cacheInstance, new Session(getDaoToSessionFactory(a).getCurrentSession()));
                // ((HibernateDAO) cacheInstance).setSession();
                log.debug("call HibernateDAO setSession(session)");
            } catch (Exception e) {
                log.warn(key + " have not setSession method ", e);
                // throw new RuntimeException
            }
        }
        return cacheInstance;
    }

    /**
     * 获取Dao
     * 
     * @param <T>
     * @param a
     *            Dao对应的Class
     * @return Dao对象
     */
    public <T> T getDAO(Class<T> a) {
        return getDAO(a, true);
    }
}
